<?php
/**
 * Base class for all behaviour handlers.
 * This class is responsible for managing behaviours, it does the following
 *  - passes method requests on to the appropriate behaviour
 *  - allows behaviours to be added
 *  - can be checked to see if behaviours have been added
 *
 * @author Rob Young <rob@roryoung.co.uk>
 * @package forage.behaviours
 */
abstract class ForageBehaviourHandler
{
  /**
   * All the ForageBehaviour objects which are in this handler
   *
   * @var ForageBehaviour[]
   */
  private $behaviours;

  /**
   * Class constructor
   */
  public function __construct()
  {
    $this->behaviours = array();
  }

  /**
   * Handle method calls passing them on to behaviours where appropriate.
   *
   * @param string $method
   * @param array $args
   * @return mixed
   */
  public function __call($method, $args)
  {
    if (method_exists($this, $method)) {
      return call_user_func_array(array($this, $method), $args);
    }
    foreach ($this->behaviours as $behaviour) {
      if (is_callable(array($behaviour, $method))) {
        return call_user_func_array(array($behaviour, $method), $args);
      }
    }
    throw new BadMethodCallException("Invalid Behaviour Method [" . $method . "] have you added all required behaviours?");
  }

  public function __get($name)
  {
    foreach ($this->behaviours as $behaviour) {
      $value = $behaviour->$name;
      if ($value !== null) {
        return $value;
      }
    }
  }

  /**
   * Add a ForageBehaviour to this handler
   *
   * @param mixed $behaviour
   */
  public function addBehaviour($behaviour)
  {
    if (!($behaviour instanceof ForageBehaviour)) {
      throw new InvalidArgumentException("Expecting a ForageBehaviour");
    }
    if (!$this->hasBehaviour($behaviour)) {
      $this->behaviours[] = $behaviour;
    }
  }

  /**
   * Check if a given ForageBehaviour has been added to this handler.
   *
   * @param mixed $behaviour Either a ForageBehaviour object or it's class name
   * @return boolean
   */
  public function hasBehaviour($behaviour)
  {
    $class = $this->getClass($behaviour);
    foreach ($this->behaviours as $has_behaviour) {
      if (get_class($has_behaviour) == $class) {
        return true;
      }
    }
    return false;
  }

  /**
   * Remove a given ForageBehaviour if it's been added.
   *
   * @param mixed $behaviour Either a ForageBehaviour object or it's class name
   * @return boolean
   */
  public function removeBehaviour($behaviour)
  {
    $class = $this->getClass($behaviour);
    foreach ($this->behaviours as $i => $has_behaviour) {
      if (get_class($has_behaviour) == $class) {
        array_splice($this->behaviours, $i, 1);
        return true;
      }
    }
    return false;
  }

  /**
   * Convert a $behaviour paramter into a class name
   *
   * @param mixed $behaviour
   * @return string
   */
  protected function getClass($behaviour)
  {
    if (is_object($behaviour)) {
      return get_class($behaviour);
    } else {
      return $behaviour;
    }
  }
}
